﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.IO;
using System.Net.Mime;
using System.Security.AccessControl;

namespace dasBlog.Storage.Ntfs
{
    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single)]
    public class StreamStorageProvider : IStreamStorageProvider, IInitStorageProvider
    {
        #region IStreamStorageNode Members
        string storageDirectory;
        DirectoryInfo rootInfo;
        bool init = false;

        public StreamStorageProvider()
        {
        }

        public void Initialize(string initData)
        {
            initData = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,initData);
            this.storageDirectory = initData;
            if (!Directory.Exists(this.storageDirectory))
            {
                Directory.CreateDirectory(this.storageDirectory);
            }
            this.rootInfo = new DirectoryInfo(this.storageDirectory);
            init = true;
        }

        void EnsureInit()
        {
            if (!init)
            {
                this.Initialize(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), ".\\dasBlog\\binaries"));
            }
        }

        public IEnumerable<StreamStorageInfo> Select(Moniker contextId, MonikerMatch contextMatch, QueryDescription query)
        {
            EnsureInit();
            
            DirectoryInfo info = new DirectoryInfo(GetDirectoryNameForContext(contextId));
            if (info.Exists)
            {
                return from FileInfo file in info.GetFiles()
                       where !file.Name.EndsWith(".categories")
                       select new StreamStorageInfo
                       {
                           Id = new Moniker(contextId, file.Name),
                           Size = file.Length,
                           Created = file.CreationTimeUtc,
                           ContentType = MediaTypeNames.Application.Octet,
                           Categories = GetCategoriesForFile(file.FullName)
                       };
            }
            else
            {
                return null;
            }
        }



        private static List<string> GetCategoriesForFile(string fileName)
        {
            List<string> categories = new List<string>();
            try
            {
                if (File.Exists(fileName + ".categories"))
                {
                    using (StreamReader categoriesStream = new StreamReader(fileName + ".categories", Encoding.UTF8))
                    {
                        while (!categoriesStream.EndOfStream)
                        {
                            categories.Add(categoriesStream.ReadLine());
                        }
                    }
                }
                return categories;
            }
            catch (FileNotFoundException)
            {
                return categories;
            }
            catch
            {
                throw;
            }
        }

        public StreamStorageSetReply SetStream(StreamStorageSetRequest addRequest)
        {
            EnsureInit();
            Moniker contextUri = addRequest.moniker;
            DirectoryInfo info = new DirectoryInfo(GetDirectoryNameForContext(contextUri));
            if (!info.Exists)
            {
                info.Create();
            }

            string fileName = Path.Combine(info.FullName, addRequest.name);
            using (FileStream fileStream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
            {
                int bytesRead;
                byte[] buffer = new byte[64 * 1024];
                fileStream.SetLength(0);

                while ((bytesRead = addRequest.Stream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    fileStream.Write(buffer, 0, bytesRead);
                }
            }

            WriteCategoriesForFile(fileName, addRequest.categories);

            return new StreamStorageSetReply
            {
                StreamInfo = Get(new Moniker(contextUri, addRequest.name))
            };
        }


        public StreamStorageGetReply GetStream(StreamStorageGetRequest getRequest)
        {
            EnsureInit();
            Moniker itemId = getRequest.itemId;
            DirectoryInfo info = new DirectoryInfo(GetDirectoryNameForContext(itemId));
            var fileInfo = info.GetFiles(itemId.ItemId);
            if (fileInfo.Length > 0)
            {
                return new StreamStorageGetReply
                {
                    StreamInfo = new StreamStorageInfo
                    {
                        ContentType = MediaTypeNames.Application.Octet,
                        Id = itemId,
                        Size = fileInfo[0].Length,
                        Created = fileInfo[0].CreationTimeUtc,
                        Categories = GetCategoriesForFile(fileInfo[0].FullName)
                    },
                    Stream = fileInfo[0].Open(FileMode.Open, FileAccess.Read, FileShare.Read)
                };
            }
            else
            {
                return null;
            }
        }

        private static void WriteCategoriesForFile(string fileName, IEnumerable<string> categories)
        {
            using (TextWriter categoriesStream = new StreamWriter(fileName + ".categories", false, Encoding.UTF8))
            {
                if (categories != null)
                {
                    foreach (string s in categories)
                    {
                        categoriesStream.WriteLine(s);
                    }
                }
            }
        }

        public void Delete(Moniker itemId)
        {
            EnsureInit();
            DirectoryInfo info = new DirectoryInfo(GetDirectoryNameForContext(itemId));
            if (info.Exists)
            {
                File.Delete(Path.Combine(info.FullName, itemId.ItemId));
            }
        }


        private string GetDirectoryNameForContext(Moniker contextId)
        {
            string path = Path.Combine(rootInfo.FullName, contextId.Scope);
            foreach (var segment in contextId.Segments)
            {
                if (segment.Id != null && !segment.IsLast)
                {
                    path = Path.Combine(Path.Combine(path, segment.Name.Value), segment.Id);
                }
                else
                {
                    path = Path.Combine(path, segment.Name.Value);
                }
            }
            return path;
        }


        #endregion

        #region IStorageProvider<StreamStorageInfo> Members


        public StreamStorageInfo Get(Moniker itemId)
        {
            DirectoryInfo info = new DirectoryInfo(GetDirectoryNameForContext(itemId));
            var fileInfo = info.GetFiles(itemId.ItemId);
            if (fileInfo.Length > 0)
            {
                return new StreamStorageInfo
                    {
                        ContentType = MediaTypeNames.Application.Octet,
                        Id = itemId,
                        Size = fileInfo[0].Length,
                        Created = fileInfo[0].CreationTimeUtc,
                        Categories = GetCategoriesForFile(fileInfo[0].FullName)
                    };
            }
            else
            {
                return null;
            }
        }


        public Moniker Store(Moniker contextId, StreamStorageInfo item)
        {
            throw new NotSupportedException();
        }

        public void Update(StreamStorageInfo item)
        {
            throw new NotSupportedException();
        }

        #endregion


        public IEnumerable<PropertyAggregate> Aggregate(Moniker contextId, MonikerMatch contextMatch, string propertyName)
        {
            EnsureInit();
            
            DirectoryInfo info = new DirectoryInfo(GetDirectoryNameForContext(contextId));
            if (info.Exists)
            {
                return from file in info.GetFiles()
                       from tag in GetCategoriesForFile(file.FullName)
                       group tag by tag into tags
                       select new PropertyAggregate { Value = tags.Key, Count = tags.Count() };
            }
            else
            {
                return null;
            }
        }



    }
}
